package com.studerb.tasks.headers;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.apache.commons.lang.math.RandomUtils;
import org.apache.commons.net.io.DotTerminatedMessageReader;
import org.apache.commons.net.nntp.NNTPCommand;
import org.apache.commons.net.nntp.NNTPReply;
import org.springframework.beans.factory.annotation.Autowired;

import com.studerb.model.Header;
import com.studerb.model.HeaderThread;
import com.studerb.model.Post;
import com.studerb.service.interfaces.IFileService;
import com.studerb.service.interfaces.IHeaderService;
import com.studerb.tasks.AbstractTask;

public class ArticleCommandTask extends AbstractTask<Void, Void> {
	@Autowired private IHeaderService headerService;
	@Autowired private IFileService fileService;

	private final List<File> outputFiles = new ArrayList<File>();
	private Post post;

	@Override protected void cancelled() {
		logger.debug("cancelled - removing temp files");
		for (File file : this.outputFiles) {
			try {
				if (!file.delete()) {
					logger.warn("Error deleting file: " + file.getAbsolutePath());
				}
			}
			catch (Exception e) {
				e.printStackTrace();
				logger.warn("Caught ioexception: " + ExceptionUtils.getRootCauseMessage(e));
			}
		}
		super.cancelled();
	}

	private void checkGroupCommand() throws IOException {
		// check if we're already on the correct group
		if (post.getNewsGroup().getGroupName().equals(nntpConnection.getCurrentGroup())) {
			return;
		}
		setProgress(5);
		setMessage("Sending Group Command");
		groupCommandReply = nntpConnection.groupCommand(post.getNewsGroup().getGroupName());
		logger.debug("Group command reply: " + nntpConnection.getReplyString());
		setMessage("Got Reply: " + nntpConnection.getReplyString());
	}

	private void checkProgress() {
		setProgress(RandomUtils.nextInt(100));
	}

	@Override protected Void doInBackground() throws Exception {
		List<Header> headers;
		// if multipart
		if (post instanceof HeaderThread) {
			logger.debug("Post is headerThread ...");
			HeaderThread ht = (HeaderThread) post;
			headers = headerService.getHeadersByThread(ht);
			logger.debug("Got " + headers.size() + " headers for post: ");
		}
		else {
			logger.debug("Post is single Header...");
			Header header = (Header) post;
			headers = Arrays.asList(new Header[] { header });
		}
		checkGroupCommand();

		for (Header header : headers) {
			if (isCancelled()) {
				logger.info("Cancelled...");
				return null;
			}
			checkPaused();
			outputFiles.add(downloadSingleArticle(header));
			checkProgress();
		}

		headerService.setArticleDownloaded(post);
		logger.debug("Downloaded " + outputFiles.size() + " single articles");

		// File encodedFile = decodingService.decodeArticle(header,
		// output.toByteArray());
		// decodedFile.getAbsolutePath(), header.getSubject()));
		return null;
	}

	private File downloadSingleArticle(Header header) throws Exception {
		DotTerminatedMessageReader reader = null;
		File outputFile = null;

		try {
			setMessage("Sending 'article' nntp command");
			int reply = nntpConnection.sendCommand(NNTPCommand.ARTICLE, header.getArticleNumber().toString());
			if (reply != NNTPReply.ARTICLE_RETRIEVED_HEAD_AND_BODY_FOLLOW) {
				setMessage("Bad reply from article: " + nntpConnection.getReplyString());
				throw new IOException("Bad reply to article command: " + nntpConnection.getReplyString());
			}
			reader = new DotTerminatedMessageReader(nntpConnection.getReader());
			setMessage("Downloading ARTICLE " + header.getArticleNumber());
			byte[] articleBytes = IOUtils.toByteArray(reader);
			outputFile = fileService.createArticleFile(header, articleBytes);
			return outputFile;
		}
		finally {
			IOUtils.closeQuietly(reader);
		}
	}

	public Post getPost() {
		return post;
	}

	@Override public void init() {
		title = "Downloading Article";
		determinable = true;
		connectionNeeded = true;
	}

	public void setPost(Post post) {
		this.post = post;
	}
}
